﻿using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Primitives;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Serialization;
using JSon = System.Text.Json.JsonSerializer;

namespace Lib.ConfigurationBase;
/// <summary>
/// 
/// </summary>
public static class ConfigsBase {
    /// <summary>
    /// 构造函数(静态)
    /// </summary>
    static ConfigsBase() {

    }
    /// <summary>
    /// 
    /// </summary>
    private static JsonSerializerOptions JavaScriptOptions = new JsonSerializerOptions() {
        // 处理中文转义问题（转义不安全字符） 
        Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
        // 不使用驼峰样式
        PropertyNamingPolicy = null,
        // 忽略循环引用
        ReferenceHandler = ReferenceHandler.IgnoreCycles,
        // 处理注释
        ReadCommentHandling = JsonCommentHandling.Skip,
        // 忽略大小写
        PropertyNameCaseInsensitive = true,
        // 缩进
        WriteIndented = true
    };
    /// <summary>
    /// 
    /// </summary>
    private static Dictionary<string, long> ChangeTokenSeconds { get; set; } = [];
    /// <summary>
    /// 读取配置文件 返回 IConfigurationRoot 对象（SubKey = null 时，配置文件内容不能是数组格式）
    /// </summary>
    /// <param name="File">配置文件路径（格式：“E:/Configs/Option”或“Option”或“/Option”或“@Option”）</param>
    /// <param name="SubKey">配置节点关键字（格式：“SectionOne”或“SectionOne:KeyOne”）</param>
    /// <param name="Action"></param>
    /// <returns></returns>
    public static IConfigurationRoot InitConfigurationRoot(string File, string SubKey = null, Action<IConfigurationRoot, string, string, bool> Action = null) {
        //
        var Key = Parse(File);
        //
        try {
            File = $"{Key}.json";
            if (System.IO.File.Exists(File)) {
                var Builder = new ConfigurationBuilder();
                var Root = Builder.AddJsonFile(path: File, optional: false, reloadOnChange: true).Build();
                ChangeTokenSeconds.TryAdd(Key, DateTime.Now.Ticks / 10000);
                Action?.Invoke(Root, Key, SubKey, false);
                ChangeToken.OnChange(() => Root.GetReloadToken(), () => {
                    var MilliSeconds = ChangeTokenSeconds[Key];
                    if (((DateTime.Now.Ticks / 10000) - MilliSeconds) > 1000) {
                        Action?.Invoke(Root, Key, SubKey, true); ChangeTokenSeconds[Key] = DateTime.Now.Ticks / 10000;
                    }
                });
                return Root;
            } else {
                Console.WriteLine($"\n{DateTime.Now:yyyy-MM-dd HH:mm:ss} 加载配置文件“{File}”出错，文件不存在!\n");
            }
        } catch (Exception Ex) {
            Ex.Exception();
        }
        return null;
    }
    /// <summary>
    /// 解析文件路径
    /// </summary>
    /// <param name="File"></param>
    /// <returns></returns>
    private static string Parse(string File) {
        File = File.Replace(@"\", "/"); 
        if (File.EndsWith(".json", StringComparison.OrdinalIgnoreCase)) {
            File = File[..^5];
        }
        if (File.Contains(":/")) {

        } else {
            var BaseDirectory = $"{AppDomain.CurrentDomain.BaseDirectory.Replace(@"\", "/").TrimEnd('/')}";
            if (File.StartsWith('@')) {
                BaseDirectory = $"{BaseDirectory}/{"Configs"}";
            }
            File = $"{BaseDirectory}/{File.TrimStart('@').TrimStart('/')}";
        }
        return File;
    }
    /// <summary>
    /// 线程锁
    /// </summary>
    private static readonly Lock Lock = new();
    /// <summary>
    /// 读取配置文件绑定对象（SubKey = null 时，返回对象不能为数组与列表）
    /// </summary>
    /// <param name="Type">返回对象类型</param>
    /// <param name="e">返回对象</param>
    /// <param name="File">配置文件路径（格式：“E:/Configs/Option”或“Option”或“/Option”或“@Option”）</param>
    /// <param name="SubKey">配置节点关键字（格式：“SectionOne”或“SectionOne:KeyOne”）</param>
    /// <returns></returns>
    public static object Initialize(Type Type, object e, string File, string SubKey = null) {
        try {
            var Root = InitConfigurationRoot(File, SubKey, (Root, File, SubKey, Reload) => {
                Root?.Initialize(Type, e, File, SubKey, Reload);
            });
        } catch (Exception Ex) {
            Ex.Exception();
        }
        return e;
    }
    /// <summary>
    /// 读取配置文件绑定对象（SubKey = null 时，返回对象不能为数组与列表）
    /// </summary>
    /// <param name="Root">ConfigurationRoot 对象</param>
    /// <param name="Type">返回对象类型</param>
    /// <param name="e">返回对象</param>
    /// <param name="File">配置文件路径（格式：“E:/Configs/Option”或“Option”或“/Option”或“@Option”）</param>
    /// <param name="SubKey">配置节点关键字（格式：“SectionOne”或“SectionOne:KeyOne”）</param>
    /// <param name="Reload"></param>
    public static void Initialize(this IConfigurationRoot Root, Type Type, object e, string File = null, string SubKey = null, bool Reload = false) {
        lock (Lock) {
            if (string.IsNullOrEmpty(SubKey)) {
                Root.Bind(e);
            } else {
                Root.Bind(SubKey, e);
            }
            if (e != null) {
                //
                Type?.GetMethod("Initialize")?.Invoke(e, null);
                //
                Type?.GetField("ConfigurationRoot")?.SetValue(e, Root);
            }
            Console.WriteLine($"\n{DateTime.Now:yyyy-MM-dd HH:mm:ss} 配置文件“{File}.json”已{(Reload ? "重载" : "加载")}成功!\n");
        }
    }
    /// <summary>
    /// 读取配置文件绑定对象（SubKey = null 时，返回对象不能为数组与列表）
    /// </summary>
    /// <typeparam name="T">返回对象</typeparam>
    /// <param name="File">配置文件路径（格式：“E:/Configs/Option”或“Option”或“/Option”或“@Option”）</param>
    /// <param name="SubKey">配置节点关键字（格式：“SectionOne”或“SectionOne:KeyOne”）</param>
    /// <param name="Action"></param>
    /// <returns></returns>
    public static T Initialize<T>(string File, string SubKey = null, Action<IConfigurationRoot, string, string, bool> Action = null) {
        var Type = typeof(T);
        var e = default(T);
        if (Type.IsClass && Type.Name != "String") {
            e = (T)Activator.CreateInstance(Type); 
        }
        try {
            var Root = InitConfigurationRoot(File, SubKey, (Root, File, SubKey, Reload) => {
                //
                Root?.Initialize<T>(e, File, SubKey, Reload); 
                //
                Action?.Invoke(Root, File, SubKey, Reload);
            });
        } catch (Exception Ex) {
            Ex.Exception();
        }
        return e;
    }
    /// <summary>
    /// 读取配置文件绑定对象
    /// </summary>
    private static void Initialize<T>(this IConfigurationRoot Root, T e, string File, string SubKey = null, bool Reload = false)  {
        lock (Lock) {
            if (string.IsNullOrEmpty(SubKey)) {
                Root.Bind(e);
            } else {
                Root.Bind(SubKey, e);
            }
            var Type = typeof(T);
            if (e != null) {
                //
                Type.GetMethod("Initialize")?.Invoke(e, null);
                //
                Type.GetField("ConfigurationRoot")?.SetValue(e, Root);
            }
            Console.WriteLine($"\n{DateTime.Now:yyyy-MM-dd HH:mm:ss} 配置文件“{File}.json”已{(Reload ? "重载" : "加载")}成功!\n");
        }
    }
    /// <summary>
    /// 保存配置文件
    /// </summary>
    public static bool SaveChanges<T>(T That, string File) {
        if (That != null) {
            File = Parse(File);
            try {
                var Content = JSon.Serialize(That, JavaScriptOptions);
                if (!string.IsNullOrEmpty(Content)) {
                    //
                    System.IO.File.WriteAllText($"{File}.json", Content, Encoding.UTF8); 
                    //
                    return true;
                }
            } catch {
                Console.WriteLine($"\n{DateTime.Now:yyyy-MM-dd HH:mm:ss} 配置文件“{File}”保存出错!\n");
            }
        }
        return false;
    }
    /// <summary>
    /// 控制台错误信息输出
    /// </summary>
    internal static bool Exception(this Exception Ex) {
        Console.WriteLine("错误信息：" + Ex.Message);
        Console.WriteLine("异常对象：" + Ex.Source);
        Console.WriteLine("调用堆栈：" + Ex.StackTrace);
        Console.WriteLine("触发类名：" + Ex.TargetSite?.DeclaringType?.FullName);
        Console.WriteLine("触发方法：" + Ex.TargetSite?.Name);
        return false;
    }
}